/**
 *
 */
package com.angel.io.processors.commands.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;

import com.angel.common.helpers.StringHelper;
import com.angel.io.descriptor.ExportFileProcessorDescriptor;
import com.angel.io.descriptor.ExportedFileDescriptor;
import com.angel.io.exceptions.InvalidFileAccessException;
import com.angel.io.exceptions.InvalidFileDataException;
import com.angel.io.processors.commands.exports.ExportFileProcessorCommand;
import com.angel.io.processors.commands.imports.ImportFileProcessorCommand;
import com.angel.io.processors.commands.imports.ImportRowProcessorCommand;
import com.angel.io.separator.ColumnSeparator;
import com.angel.io.type.rows.ExportRowFile;
import com.angel.io.type.rows.ImportRowFile;
import com.angel.io.type.rows.impl.HeaderRowFile;
import com.angel.io.type.rows.impl.PlainRowFile;

/**
 * @author William
 *
 */
public class PlainFileProcessorCommand implements ImportFileProcessorCommand, ExportFileProcessorCommand{

	private static final Character FINAL_LINE_CHARACTER = '\n';
	private static final Character RETURN_LINE_CHARACTER = '\r';

	private static final String FILE_EXTENSION_SEPARATOR = ".";
	private static final String DEFAULT_FILE_EXTENSION = FILE_EXTENSION_SEPARATOR + "txt";
	private ImportRowProcessorCommand command;
	private InputStreamReader reader;
	private String fileExtension;

	public PlainFileProcessorCommand(){
		super();
		this.fileExtension = DEFAULT_FILE_EXTENSION;
	}

	public PlainFileProcessorCommand(ImportRowProcessorCommand command, String fileExtension){
		super();
		this.command = command;
		this.fileExtension = this.buildFileExtension(fileExtension);
	}

	private String buildFileExtension(String fileExtension) {
		if(fileExtension == null){
			return DEFAULT_FILE_EXTENSION;
		} else {
			fileExtension = fileExtension.trim();
			if(fileExtension.length() == 0){
				return DEFAULT_FILE_EXTENSION;
			} else {
				fileExtension = fileExtension.toUpperCase();
				fileExtension = fileExtension.replaceAll(FILE_EXTENSION_SEPARATOR, StringHelper.EMPTY_STRING);
				fileExtension = FILE_EXTENSION_SEPARATOR + fileExtension;
				return fileExtension;
			}
		}
	}

	protected Character getNewLineChar(){
		return FINAL_LINE_CHARACTER;
	}

	protected Character getReturnChar(){
		return RETURN_LINE_CHARACTER;
	}

	protected boolean isNewLineChar(int character){
		return character == getNewLineChar().charValue() || character == getNewLineChar().charValue();
	}

	protected boolean isReturnLineChar(int character){
		return character == this.getReturnChar().charValue() || character == this.getReturnChar().charValue();
	}

	public List<ImportRowFile> processFile(Integer numberPage) throws InvalidFileDataException{
		List<ImportRowFile> allRowFiles = new ArrayList<ImportRowFile>();;
		try {
			StringBuffer line = new StringBuffer();
			int charRead = 0;
			while((charRead = reader.read()) != -1 ){
				if(!this.isReturnLineChar(charRead) && !this.isNewLineChar(charRead)){
					line.append((char)charRead);
				} else {
					if(this.isNewLineChar(charRead)){
						ImportRowFile rowFile = new PlainRowFile(line.toString());
						allRowFiles.add(rowFile);
						line.delete(0, line.length());
					}
				}
			}
		} catch (IOException e) {
			throw new InvalidFileAccessException(e);
		}
		return allRowFiles;
	}


	/**
	 * @return the command
	 */
	public ImportRowProcessorCommand getCommand() {
		return command;
	}

	/**
	 * @param command the command to set
	 */
	public void setCommand(ImportRowProcessorCommand command) {
		this.command = command;
	}

	public void finish() {
		try {
			reader.close();
		} catch (IOException e) {
			throw new InvalidFileDataException("An error ocurred when close input stream reader.", e);
		}
	}

	public void initialize(InputStream inputStream) throws InvalidFileDataException {
		this.reader = new InputStreamReader(inputStream);

	}

	public ExportRowFile createHeader(HeaderRowFile header, ColumnSeparator columnSeparator) {
		String headerRow = this.plainHeaderColumn(header, columnSeparator);
		ExportRowFile exportHeader = new PlainRowFile(headerRow, columnSeparator);
		for(int i = 0; i < header.findMaxPosition().intValue(); i++){
			String columnName = header.getColumnName(i);
			Integer position = header.getColumnPosition(columnName);
			exportHeader.addStringValue(columnName, position);
		}
		return exportHeader;
	}

	private String plainHeaderColumn(HeaderRowFile header, ColumnSeparator columnSeparator){
		List<String> columnsNames = new ArrayList<String>();
		Integer maxPosition = header.findMaxPosition();
		for(int i = 0; i < maxPosition; i++){
			if(i == maxPosition - 1){
				columnsNames.add(header.getColumnName(i));
			} else {
				columnsNames.add(header.getColumnName(i) + columnSeparator.getValue());
			}
		}
		return StringHelper.convertToPlainString(columnsNames);
	}

	public void finish(ExportedFileDescriptor exportedFileDescriptor, ExportFileProcessorDescriptor fileProcessorDescriptor, HeaderRowFile header, List<ExportRowFile> exportRows) {
		try {
			OutputStream os = fileProcessorDescriptor.createOutputStream(exportedFileDescriptor, this.getFileExtension());
			OutputStreamWriter osw = new OutputStreamWriter(os);

			osw.write(header.prepareHeader(fileProcessorDescriptor.getColumnSeparator()) + "\n");

			for(ExportRowFile exportRow: exportRows){
				osw.write(exportRow.export() + "\n");
			}
			osw.flush();
			osw.close();
		} catch (IOException e) {
			throw new InvalidFileAccessException("IO error ocurred when finish output stream. ", e);
		}
	}

	public void initialize() {
		//Do nothing
	}

	public List<ExportRowFile> prepareFile(HeaderRowFile header, Integer quantityRows, ColumnSeparator columnSeparator) {
		List<ExportRowFile> exportRows = new ArrayList<ExportRowFile>();
		for(int i = 0; i < quantityRows; i++){

			ExportRowFile exportHeader = new PlainRowFile(columnSeparator, header.quantityColumns());
			exportRows.add(exportHeader);
		}
		return exportRows;
	}


	public String getFileExtension() {
		return this.fileExtension;
	}
}
